/*
 * Copyright 2009 Andreas Borglin andreas.borglin@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package borge.mobile.lib.pimp;

import java.io.ByteArrayInputStream;
import java.io.InputStream;


/**
 * ImageManager is the main interface used to handle sessions
 * and image manipulations. It supports preview mode and writing
 * those changes to file.
 * 
 * @author Andreas Borglin
 */
public class ImageManager implements ImageConstants{
    
    private PreviewHandler preview;
    private RealHandler real;
    
    /**
     * Create a new instance of ImageManager
     */
    public ImageManager() {
    
        preview = new PreviewHandler();
        real = new RealHandler();
    }
    
    /**
     * Check if the underlying platform has the support we need
     * to be able to execute
     * @return True if it has the necessary support. False if not.
     */
    public static boolean isPlatformSupported(){
        
        // We only have image support via JSR-234 currently
        String prop = System.getProperty("supports.mediacapabilities");
        
        if(prop == null)
            return false;
        
        if(prop.indexOf("imagepostprocessing") == -1)
            return false;
        
        return true;
        
    }
    
    /**
     * Query the framework on supported input formats.
     * @return A string array of supported input formats/mime types
     */
    public String[] getSupportedInputFormats(){
        return preview.getSupportedInputFormats();
    }
    
    /**
     * Query the framework on supported output formats.
     * The resulting file will be saved/converted to this format.
     * @return A string array of supported output formats/mime types
     */
    public String[] getSupportedOutputFormats(){
        return preview.getSupportedOutputFormats();
    }
    
    /**
     * Get an instance of ImageSupport object that contains
     * information on what is supported on the current phone.
     * @return An instance to an ImageSupport object
     */
    public ImageSupport getSupportInfo(){
        return preview.getSupportInfo();
    }
    
    
    /**
     * Initialize the image manager with input file url and area bounds
     * on which the preview image must fit.
     * @param fileUrl - Full URL to the input image file
     * @throws borge.mobile.lib.pimp.ImageEffectException
     */
    public void initialize(String fileUrl) 
            throws ImageEffectException {
        preview.initialize(fileUrl);
    }
    
    /**
     * Get the path to the input image.
     * @return Path to input image
     */
    public String getInputImagePath(){
        return preview.getInputFile();
    }
    
    /**
     * Since it is very likely that the input image is larger than the screen,
     * the image we work with have to be rescaled. This method can be used to
     * scale the preview image to the screen, while keeping the aspect ratio.
     * @param areaWidth Screen width
     * @param areaHeight Screen height
     * @throws borge.mobile.lib.pimp.ImageEffectException
     */
    public void scalePreviewImage(int areaWidth, int areaHeight) throws ImageEffectException{
        preview.scaleImage(areaWidth, areaHeight);
    }
    
    /**
     * Image object are dependent on underlying UI technology, so it is up
     * to the application to create an Image object from image data.
     * @param ic The class implementing ImageCreator interface
     * @throws borge.mobile.lib.pimp.ImageEffectException
     */
    public void setImageCreator(ImageCreator ic) throws ImageEffectException{
        preview.setImageCreator(ic);
    }

    /**
     * Find out if any change has been made that could
     * be applied to the real image.
     * @return true if changes have been made, false otherwise.
     */
    public boolean changesMade(){
        return preview.changesMade();
    }
    /**
     * Undo the latest operation.
     * Only supported for preview mode.
     * @return True if success, false if it failed.
     */
    public boolean undo(){
        return preview.undo();
    }
    
   /**
    * Query the framework if we can undo the latest change.
    * @return True if we can undo, false it not.
    */
    public boolean canUndo(){
        return preview.canUndo();
    }

    /**
     * Redo the latest undo
     * NOT SUPPORTED
     * @return True if success, false if not.
     */
    public boolean redo(){
        return preview.redo();
    }
    
    /**
     * Commit the changes which will update the image data.
     * @throws borge.mobile.lib.pimp.ImageEffectException
     */
    public void applyChanges() throws ImageEffectException {

        preview.applyChanges();
    }
    
    /**
     * Reset to initialized state.
     * All actions will be undone.
     */
    public void reset(){
        preview.reset();
    }
    
    /**
     * Get an inputstream to the current image preview data
     * @return An inputstream to the current image preview data
     */
    public InputStream getCurrentImageStream(){
        return new ByteArrayInputStream(getCurrentImageData());
    }
    
    /**
     * Get the current image preview data
     * @return A byte-array containing the image data
     */
    public byte[] getCurrentImageData(){
        return preview.getCurrentImageData();
    }
    
    /**
     * Resize the image.
     * @param newWidth - The new image width
     * @param newHeight - The new image height
     * @param keepAspectRatio - If aspect ratio can not be changed
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opResize(int newWidth, int newHeight, boolean keepAspectRatio) throws ImageEffectException {
        preview.resize(newWidth, newHeight, keepAspectRatio);
    }
    
        /**
     * Resize the image.
     * @param percent - Percent of orginal image
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opResize(int percent) throws ImageEffectException {
        preview.resize(percent);
    }
    
    /**
     * Rotate the image 90,180 or 270 degrees.
     * The input value must be one of the ImageSupport rotation values:
     * ROTATE_90,
     * ROTATE_180,
     * ROTATE_270
     * @param rotation - The rotation value
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opRotate(int rotation) throws ImageEffectException{
        
        preview.rotate(rotation);
    }
    
    /**
     * Flip the image horizontally or vertically.
     * The input value must be one of the ImageSupport flip values:
     * FLIP_HORIZONTALLY,
     * FLIP_VERTICALLY
     * @param direction - The flip value
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opFlip(int direction) throws ImageEffectException {
        
        preview.flip(direction);
    }
    
    /**
     * Crop the image (Choose a part of the image to be used).
     * @param startX - Start X coordinate in the image
     * @param startY - Start Y coordinate in the image
     * @param width - Width of the cropped image
     * @param height - Height of the cropped image
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opCrop(int startX, int startY, int width, int height) throws ImageEffectException {
        
        preview.crop(startX, startY, width, height);
    }
    
    /**
     * Give the image a black frame.
     * @param frameWidth - The frame width
     * @param frameHeight - The frame height
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opFrame(int frameWidth, int frameHeight) throws ImageEffectException {
        
        preview.frame(frameWidth, frameHeight);
    }
    
    /**
     * Change the brightness of the image
     * @param brightnessValue - Brightness value in the range reported in its ImageOperation
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opSetBrightness(int brightnessValue) throws ImageEffectException {
        
        preview.setBrightness(brightnessValue);
    }
    
    /**
     * Change the contrast of the image.
     * @param contrastValue - Contrast value in the range reported in its ImageOperation
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opSetContrast(int contrastValue) throws ImageEffectException {
        
        preview.setContrast(contrastValue);
    }
    
    /**
     * Change the gamma of the image.
     * @param gammaValue - Gamma value in the range reported in its ImageOperation
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opSetGamma(int gammaValue) throws ImageEffectException {
        
        preview.setGamma(gammaValue);
    }
    
    /**
     * Change the white balance of the image.
     * @param index - Index to one of the supported white balance modes
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opSetWhiteBalance(int index) throws ImageEffectException {
        preview.setWhiteBalance(index);
    }
    
    /**
     * Apply an effect to the image.
     * @param index - Index to the desired effect
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opApplyEffect(int index) throws ImageEffectException {
        preview.applyEffect(index);
    }
    
    /**
     * Create an overlay image from a file URL.
     * @param imageURL - The url to the overlay image file
     * @return - An OverlayBounds instance with the overlay image bounds
     * @throws borge.mobile.lib.pimp.ImageEffectException
     */
    public OverlayBounds createOverlayImage(String imageURL) throws ImageEffectException{
        
        return preview.createAndScaleOverlayImage(imageURL);
  
    }
    
    /**
     * Add a created overlay to a certain point in the image.
     * createOverlayImage must be called prior to calling this function!
     * @param x - The X coordinate of where to render the image
     * @param y - The Y coordinate of where to render the image
     * @throws borge.mobile.lib.pimp.ImageEffectException
     * If not supported, incorrect arguments or an error occurs
     */
    public void opAddOverlayImage(int x, int y) throws ImageEffectException{
        preview.addOverlayImage(x, y);
    }
    
     
    /**
     * Check if a certain file extension is supported
     * @param file - The image file name
     * @return - True if it is supported. False it it is not.
     */
    public boolean isValidFileType(String file){
        
        String elem = file.toLowerCase();
        
        for(int i = 0; i < BaseHandler.SUPPORTED_FILETYPES.length; ++i){
            
            if(elem.endsWith(BaseHandler.SUPPORTED_FILETYPES[i]))
                return true;
            
        }

        return false;
        
    }

    /**
     * Get the scaled preview image width.
     * @return The preview image width
     */
    public int getPreviewImageWidth() {
        return preview.getImageWidth();
    }

     /**
     * Get the scaled preview image height.
     * @return The preview image height.
     */
    public int getPreviewImageHeight() {
        return preview.getImageHeight();
    }
    
    /**
     * When user resizes image in preview mode, we never resize
     * preview image, and real image will not be resized until
     * user chooses the save action.
     * This method will return the current width that the real image
     * would have if user chooses to save at this stage.
     * If user has not resized, it will return orignal width.
     * @return Current real image width
     */
    public int getCurrentRealWidth(){
        return preview.getResizedRealImageWidth();
    }
    
    /**
     * When user resizes image in preview mode, we never resize
     * preview image, and real image will not be resized until
     * user chooses the save action.
     * This method will return the current height that the real image
     * would have if user chooses to save at this stage.
     * If user has not resized, it will return orignal height.
     * @return Current real image height
     */
    public int getCurrentRealHeight(){
        return preview.getResizedRealImageHeight();
    }
    
     /**
     * Get the real image original width.
     * @return The real image width
     */
    public int getRealImageWidth(){
        return real.getImageWidth();
    }
    
     /**
     * Get the real image original height.
     * @return The real image height
     */
    public int getRealImageHeight(){
        return real.getImageHeight();
    }
    
    /**
     * The preview image is scaled to fit the area provided
     * in initialize. Query this method to get the scale ratio.
     * @return The preview scale ratio in percent
     */
    public int getPreviewScaleRatio(){
        return preview.getScaleRatioAsPercent();
    }
    
    /**
     * Write all performed operations to actual file and real image size.
     * @param outputFile - The output file URL to save the image in
     * @param outputFormat - The format to save the image to
     * @param progressListener - A progress listener instance
     * @throws borge.mobile.lib.pimp.ImageEffectException
     */
    public void writeChangesToFile(String outputFile, String outputFormat, 
            ProgressListener progressListener) throws ImageEffectException{
        
        // We must close preview here to release files and streams
        preview.close();
        real.reset();
        real.setPreviewValues(preview.getScaleRatio(), preview.getOperations());
        real.writeChangesToFile(preview.getInputFile(),outputFile, outputFormat, progressListener);
    }

}
